﻿using System;
using System.Linq;
using System.Collections.Generic;
using System.Reactive;
using System.Reactive.Linq;

namespace DemoRealtimeData.Service
{
    public sealed class CalculationEngine
    {
        // ------------------------------------------------- //
        #region "member field"

        private readonly IDictionary<string, int> _seeds;
        private readonly DataType[] _dtypes;
        private readonly Config _config;

        #endregion

        // ------------------------------------------------- //
        #region "constructor"

        public CalculationEngine()
            : this(Config.Default)
        {
        }

        public CalculationEngine(Config config)
        {
            _config = config;

            int seed = 0;
            _seeds = new Dictionary<string, int>();
            foreach (var dev in _config.Devices)
            {
                seed += 30;
                _seeds.Add(dev, seed);
            }

            _dtypes = new DataType[]
                          {
                              DataType.VM,
                              DataType.VA,
                              DataType.IM, 
                              DataType.IA
                          };
        }

        #endregion

        // ------------------------------------------------- //
        #region "public API"

        public IObservable<Measurement[]> Start()
        {
            return Observable
                .Interval(TimeSpan.FromMilliseconds(_config.Interval))
                .Timestamp()
                .Take(_config.Total)
                .Select(MakeMeasurements);
        }

        #endregion

        // ------------------------------------------------- //
        #region "private helpers"

        private Measurement[] MakeMeasurements(Timestamped<long> tindex)
        {
            return (from kv in _seeds
                    from dttype in _dtypes
                    select MakeMeasurement(kv, dttype, tindex)).ToArray();
        }

        private Measurement MakeMeasurement(KeyValuePair<string, int> kv, DataType dttype, Timestamped<long> tindex)
        {
            double x = kv.Value + tindex.Value;
            double y = 0;
            switch (dttype)
            {
                case DataType.VM:
                    y = Math.Sin(Math.Sin(x));
                    break;

                case DataType.VA:
                    y = Math.Sin(x);
                    break;

                case DataType.IM:
                    y = Math.Cos(Math.Cos(x));
                    break;

                case DataType.IA:
                    y = Math.Cos(x);
                    break;
            }
            return new Measurement(tindex.Timestamp.LocalDateTime, kv.Key, dttype, y);
        }

        #endregion
    }
}
